package com.nulldev.util.web.YouTubeExtractor.extractors.flex;

import java.time.Duration;
import java.util.List;
import java.util.Map;

import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.concurrency.threadIt.v4.asyncTasks.AsyncTaskCallback;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.IExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.cntft.ControllableFutureTask;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.variable;
import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.io.IOUtils;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.HttpClient.HttpClient;
import com.nulldev.util.web.HttpClient.HttpResponse;
import com.nulldev.util.web.URLUtils.Entry;
import com.nulldev.util.web.URLUtils.fuckTracking.FuckTracking;
import com.nulldev.util.web.YouTubeExtractor.YouTubeExtractor;
import com.nulldev.util.web.YouTubeExtractor.YoutubeExtractionOptions;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractor;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractorConstants;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractedVideo;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractionResult;
import com.nulldev.util.web.YouTubeExtractor.video.YouTubeVideo;

public class FlexiblePageExtractor extends IYouTubeExtractor {

	private static final IExecutorManager td = ExecutorManager.get();
	public static final String VERSION = "1.0.0-1";
	private static final Logger logger = LoggerFactory.getLoggerD(true, true);

	@Override
	public Executable<ExtractedVideo> generateTask(String videoID, YoutubeExtractionOptions... options) {
		return generateTask(videoID, null, options);
	}

	@Override
	public Executable<ExtractedVideo> generateTask(String videoID, String extraArgs, YoutubeExtractionOptions... options) {
		logger.warn("generateTask(String, String, YoutubeExtractionOptions) is deprecated and marked for removal.");
		return generateTask(IYouTubeExtractorConstants.convertFromLegacy(videoID, extraArgs, options));
	}

	private static String generateURL(final String videoID, final Map<String, Object> arguments) {
		final StringBuilder b = new StringBuilder("https://www.youtube.com/watch?v=");
		b.append(videoID);
		if (arguments.containsKey(MAP_TYPE_FORCE_ENGLISH_LANG_COMPAT))
			b.append("&hl=EN");
		if (arguments.containsKey(MAP_TYPE_ENABLE_CONTROVERSY_BYPASS)) {
			b.append("&bpctr=");
			b.append(System.currentTimeMillis());
		}
		if (arguments.containsKey(MAP_TYPE_ENABLE_VERIFIED_SPOOFING)) {
			b.append("&has_verified=1");
		}
		return b.toString();
	}

	@Override
	public Executable<ExtractedVideo> generateTask(final Map<String, Object> arguments) {
		if (!isValidMap(arguments))
			throw new IllegalArgumentException("Invalid map: " + arguments);
		final String videoID = (String) arguments.get(MAP_TYPE_VIDEO_ID);
		if (!isValidVideoID(videoID))
			throw new IllegalArgumentException("Invalid video ID: " + videoID);
		final ControllableFutureTask<ExtractedVideo> cnft = new ControllableFutureTask<ExtractedVideo>();
		final List<YouTubeVideo> results = FastLists.list(OUTPUT_LIST_DEFAULT_SIZE);
		final variable<Integer> _index = new variable<Integer>(-1);
		logger.debug("=> Generating URL...");
		final StringBuilder extras = new StringBuilder();
		if (arguments.containsKey(MAP_TYPE_PLAYLIST_STR)) {
			extras.append("&list=");
			extras.append(arguments.get(MAP_TYPE_PLAYLIST_STR));
		}
		if (arguments.containsKey(MAP_TYPE_PLAYLIST_INDEX)) {
			extras.append("&index=");
			final int ix = (int) arguments.get(MAP_TYPE_PLAYLIST_INDEX);
			extras.append(ix);
			_index.set((int) arguments.get(MAP_TYPE_PLAYLIST_INDEX));
		}
		final String url = generateURL(videoID, arguments) + extras;
		logger.debug("URL: " + url);
		Entry[] _args = (!isEnabled(arguments, MAP_TYPE_DISABLE_CONSENT_COOKIE_COMPAT)) ? YouTubeExtractor.DEFAULT_COOKIES : null;
		if (isEnabled(arguments, MAP_TYPE_SEND_FAKE_COOKIES_COMPAT) || isEnabled(arguments, MAP_TYPE_ENFORCE_PRIVACY_MODE_COMPAT)) {
			if (_args == null)
				_args = new Entry[0];
			_args = ArrayUtils.add(_args, new Entry("Cookie", StringsUtil.randomString(64)));
		}
		if (isEnabled(arguments, MAP_TYPE_ENFORCE_PRIVACY_MODE_COMPAT) || isEnabled(arguments, MAP_TYPE_EMULATE_INSECURE_PROXY_COMPAT)) {
			if (_args == null)
				_args = new Entry[0];
			_args = ArrayUtils.addAll(_args, FuckTracking.protect());
		}
		try {
			final boolean allowLateExtractions = isEnabled(arguments, MAP_TYPE_ENABLE_LATE_EXTRACTIONS);
			final boolean ECD_ENABLED = isEnabled(arguments, MAP_TYPE_ENABLE_EXPERIMENTAL_FEATURES);
			HttpClient.getString().url(url).timeout(Duration.ofSeconds(24)).headers(_args).createTaskAndRun()
					.addCallback(new AsyncTaskCallback<HttpResponse<String>>() {

						@Override
						public void onCompletion(final HttpResponse<String> _result) {
							if (_result.status() == 403) {
								IOUtils.closeQuietly(_result);
								cnft.complete(new ExtractedVideo(null, null, ExtractionResult.FAILED).setMessage("FAILED_VIDEO_NOT_FOUND")
										.setYTMessage("YouTube reports this video as non-existant."));
								return;
							}
							/**
							 * TODO: <br>
							 * - Parse the title from meta tags or JSON extraction <br>
							 * - Parse the author from meta tags or JSON extraction <br>
							 * - Fetch the videos/ciphers using JSON extraction <br>
							 * - Hook up the decipherer and DFS.
							 */
						}

						@Override
						public void onError(final Throwable e) {
							cnft.fail(e);
						}
					});
		} catch (Throwable ex) {
			cnft.fail(ex);
		}
		return cnft;
	}

	@Override
	public String getID() {
		return "flex.page";
	}

	@Override
	public int abiVersion() {
		return 0;
	}

}
